#!/bin/bash
{
	#////////////////////////////////////
	# DietPi Drive Manager
	#
	#////////////////////////////////////
	# Created by Daniel Knight / daniel.knight@dietpi.com / dietpi.com
	#
	#////////////////////////////////////
	#
	# Info:
	# - Location: /boot/dietpi/dietpi-drive_manager
	#
	# Usage:
	# - <empty>	= Interactive menu
	# - 1		= Select an available drive mount which is then saved to: /tmp/dietpi-drive_manager_selmnt
	# - 3		= Scan for new drives and re-create fstab non-interactively, then exit
	# - 4		= Reset /etc/fstab with currently attached local drives and /tmp + /var/log tmpfs mount. Used by: PREP_SYSTEM_FOR_DIETPI.sh
	#
	#////////////////////////////////////

	# Import DietPi-Globals --------------------------------------------------------------
	. /boot/dietpi/func/dietpi-globals
	readonly G_PROGRAM_NAME='DietPi-Drive_Manager'
	G_CHECK_ROOT_USER
	G_INIT
	# Import DietPi-Globals --------------------------------------------------------------

	# Start services on exit, if stopped during operation
	SERVICES_STOPPED=0
	G_EXIT_CUSTOM(){ (( $SERVICES_STOPPED )) && /boot/dietpi/dietpi-services start; }

	# Grab input
	[[ $1 == [134] ]] && INPUT=$1 || INPUT=0

	EXIT_CODE=0

	# APT pre-req check flag, allows to check for required APT packages only once per session
	APT_CHECK=0

	# Drive menu
	MENU_DRIVE_INDEX=0
	MENU_DRIVE_TARGET=

	# Format menu
	FORMAT_GPT=1 # default GPT: https://github.com/MichaIng/DietPi/issues/531. 0=MBR
	FORMAT_FILESYSTEM_TYPE=0 # 0=ext4 1=ntfs 2=fat32 3=hfs+ 4=btrfs 5=f2fs 6=exfat
	FORMAT_COMPLETED=0
	FORMAT_MODE=1 # 0=drive 1=partition

	# Drive data
	FP_USERDATA_CURRENT=
	FP_SWAPFILE_CURRENT=

	Init_New_Device(){

		((index++))

		aDRIVE_UUID[$index]=
		aDRIVE_PART_UUID[$index]=
		aDRIVE_MOUNT_SOURCE[$index]=
		aDRIVE_MOUNT_TARGET[$index]=
		aDRIVE_SOURCE_DEVICE[$index]=
		aDRIVE_FSTYPE[$index]=
		aDRIVE_SIZE_TOTAL[$index]=
		aDRIVE_SIZE_USED[$index]=
		aDRIVE_SIZE_PERCENTUSED[$index]=
		aDRIVE_ISFILESYSTEM[$index]=0
		aDRIVE_ISMOUNTED[$index]=0
		aDRIVE_ISREADONLY_CURRENTLY[$index]=0
		aDRIVE_ISNETWORKED[$index]=0
		aDRIVE_ISROM[$index]=0
		aDRIVE_ISPARTITIONTABLE[$index]=0

	}

	Destroy(){

		unset aDRIVE_UUID aDRIVE_PART_UUID
		unset aDRIVE_MOUNT_SOURCE aDRIVE_MOUNT_TARGET
		unset aDRIVE_SOURCE_DEVICE aDRIVE_FSTYPE
		unset aDRIVE_SIZE_TOTAL aDRIVE_SIZE_USED
		unset aDRIVE_SIZE_PERCENTUSED
		unset aDRIVE_ISFILESYSTEM aDRIVE_ISMOUNTED
		unset aDRIVE_ISREADONLY_CURRENTLY aDRIVE_ISNETWORKED
		unset aDRIVE_ISROM aDRIVE_ISPARTITIONTABLE

	}

	Init_Drives_and_Refresh(){

		# Reset current index and delete arrays
		local i index=-1
		Destroy

		# Obtain actual user data location on disk (follow symlinks)
		FP_USERDATA_CURRENT=$(readlink -f /mnt/dietpi_userdata)

		# Swapfile location
		FP_SWAPFILE_CURRENT=$(sed -n '/^[[:blank:]]*AUTO_SETUP_SWAPFILE_LOCATION=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)

		# Create tmp fstab
		local fp_fstab_tmp='.fstab'
		cp -a /etc/fstab $fp_fstab_tmp

		# Special mounts
		local swap_mounts tmpfs_mounts misc_mounts net_mounts

		# Mode 4: Force reset/clean fstab (PREP)
		if (( $INPUT == 4 )); then

			local var_log_size=$(sed -n '/^[[:blank:]]*AUTO_SETUP_RAMLOG_MAXSIZE=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
			tmpfs_mounts="tmpfs /tmp tmpfs noatime,lazytime,nodev,nosuid,mode=1777
tmpfs /var/log tmpfs size=${var_log_size:-50}M,noatime,lazytime,nodev,nosuid,mode=1777"

		# Else: Grab current mounts
		else

			swap_mounts=$(grep '^[[:blank:]]*[^#].*[[:blank:]]swap[[:blank:]]' $fp_fstab_tmp)
			tmpfs_mounts=$(grep '^[[:blank:]]*tmpfs[[:blank:]]' $fp_fstab_tmp)
			# ecryptfs, vboxsf, glusterfs, bind mounts
			misc_mounts=$(grep -E '^[[:blank:]]*[^#].*([[:blank:]](ecryptfs|vboxsf|glusterfs)[[:blank:]]|[[:blank:],]bind[[:blank:],])' $fp_fstab_tmp)
			# CurlFtpFS, CIFS/SMB/Samba, NFS, SSHFS
			net_mounts=$(grep -E '^[[:blank:]]*(curlftpfs|sshfs#|[^#].*[[:blank:]](cifs|nfs4?|fuse.sshfs)[[:blank:]])' $fp_fstab_tmp)

		fi

		echo "# You can use \"dietpi-drive_manager\" to setup mounts.
# NB: It overwrites and re-creates physical drive mount entries on use.
#----------------------------------------------------------------
# NETWORK
#----------------------------------------------------------------
$net_mounts

#----------------------------------------------------------------
# TMPFS
#----------------------------------------------------------------
$tmpfs_mounts

#----------------------------------------------------------------
# MISC: ecryptfs, vboxsf (VirtualBox shared folder), gluster, bind mounts
#----------------------------------------------------------------
$misc_mounts

#----------------------------------------------------------------
# SWAPFILE
#----------------------------------------------------------------
$swap_mounts

#----------------------------------------------------------------
# PHYSICAL DRIVES
#----------------------------------------------------------------" > $fp_fstab_tmp

		G_DIETPI-NOTIFY 2 'Detecting drives, please wait...'

		# Detect mounted drives via df
		# - Exclude special treated fs types: tmpfs, ecryptfs, vboxsf, glusterfs
		# - Workaround for /dev/root on RPi: Replace with actual device path $G_ROOTFS_DEV
		# - Only detect mounts with "/" in source path, which excludes other special/pseudo fs types
		# - Remove duplicates, e.g. from bind mounts and due to /dev/root conversion: https://github.com/MichaIng/DietPi/issues/2013#issuecomment-417413867
		while read -r line
		do

			Init_New_Device

			aDRIVE_ISMOUNTED[$index]=1
			aDRIVE_MOUNT_SOURCE[$index]=$line
			aDRIVE_MOUNT_TARGET[$index]=$(findmnt -no TARGET "${aDRIVE_MOUNT_SOURCE[$index]}" | head -1) # Use only first result since bind mounts lead to multiple matches
			aDRIVE_SIZE_TOTAL[$index]=$(findmnt -rno SIZE "${aDRIVE_MOUNT_TARGET[$index]}")
			aDRIVE_SIZE_USED[$index]=$(findmnt -rno USED "${aDRIVE_MOUNT_TARGET[$index]}")
			aDRIVE_SIZE_PERCENTUSED[$index]=$(findmnt -rno USE% "${aDRIVE_MOUNT_TARGET[$index]}")

			aDRIVE_SOURCE_DEVICE[$index]=$(Return_Drive_Without_Partitions "${aDRIVE_MOUNT_SOURCE[$index]}")
			[[ ${aDRIVE_MOUNT_SOURCE[$index]} == /dev/${aDRIVE_SOURCE_DEVICE[$index]} ]] || aDRIVE_ISPARTITIONTABLE[$index]=1
			aDRIVE_UUID[$index]=$(blkid -s UUID -o value "${aDRIVE_MOUNT_SOURCE[$index]}")
			(( ${aDRIVE_ISPARTITIONTABLE[$index]} )) && aDRIVE_PART_UUID[$index]=$(blkid -s PARTUUID -o value "${aDRIVE_MOUNT_SOURCE[$index]}")

			# Physical?
			if [[ -d '/sys/block/'${aDRIVE_SOURCE_DEVICE[$index]} ]]; then

				aDRIVE_FSTYPE[$index]=$(blkid -s TYPE -o value "${aDRIVE_MOUNT_SOURCE[$index]}")
				[[ ${aDRIVE_FSTYPE[$index]} ]] && aDRIVE_ISFILESYSTEM[$index]=1

			# Networked
			else

				aDRIVE_ISNETWORKED[$index]=1
				aDRIVE_FSTYPE[$index]='Net'
				aDRIVE_ISFILESYSTEM[$index]=1

			fi

			G_DIETPI-NOTIFY 2 " - Detected mounted drive: ${aDRIVE_MOUNT_SOURCE[$index]} > ${aDRIVE_MOUNT_TARGET[$index]}"

			# R/O mounted?
			# NB: We can't use -m1 for initial check as results can be:
			#	root@DietPi:~# cat /proc/mounts | grep ' / '
			#	rootfs / rootfs rw 0 0
			#	/dev/mmcblk0p2 / ext4 ro,noatime,discard,data=ordered 0 0
			if grep -q "[[:blank:]]${aDRIVE_MOUNT_TARGET[$index]}[[:blank:]].*[[:blank:]]ro," /proc/mounts; then

				aDRIVE_ISREADONLY_CURRENTLY[$index]=1

				# RootFS R/W check
				if [[ ${aDRIVE_MOUNT_TARGET[$index]} == '/' ]]; then

					if G_WHIP_YESNO "RootFS is currently set to \"Read Only (R/O)\". $G_PROGRAM_NAME requires \"Read Write (R/W)\" access to function.\n\nWould you like to re-enable R/W access on RootFS?"; then

						G_EXEC mount -v -o rw,remount "${aDRIVE_MOUNT_TARGET[$index]}"
						aDRIVE_ISREADONLY_CURRENTLY[$index]=0
						G_DIETPI-NOTIFY 0 'Remounted RootFS with R/W access'

					else

						G_DIETPI-NOTIFY 1 "RootFS is currently set to R/O. $G_PROGRAM_NAME requires R/W access to function. Aborting..."
						G_DIETPI-NOTIFY 2 'Rerun "dietpi-drive_manager" to enable RootFS R/W access.'
						exit 1

					fi

				fi

			fi

			# Add entry to fstab
			if [[ ${aDRIVE_UUID[$index]} ]]; then

				# R/W or R/O?
				# - Add rw flag to mount options. This should be default but seems to be not in rare cases: https://github.com/MichaIng/DietPi/issues/3268
				local options=',rw'
				(( ${aDRIVE_ISREADONLY_CURRENTLY[$index]} )) && options=',ro'

				# Additional FS-specific options
				# - NTFS: Enable POSIX permissions and prevent splitting write buffers into 4k chunks: https://manpages.debian.org/ntfs-3g#OPTIONS
				if [[ ${aDRIVE_FSTYPE[$index]} == 'ntfs' ]]; then

					options+=',permissions,big_writes'

				fi

				# Root/BootFS dependant flags
				# - fsck flag for RootFS, to allow check on reboot and for BootFS, since corruption here is most critical and fsck finishes in no time
				# - nofail: Allow boot to continue, if mount fails, not wanted for Root/BootFS
				# - x-systemd.automount: [ 1166.110202] systemd-fstab-generator[3512]: Ignoring automount option for root device
				#			 /boot is removed from local-fs.target by this (on Buster only?), allowing it to mount after RAMdisk starts and unmount before it stops.
				# Source device entry
				local dev_entry="UUID=${aDRIVE_UUID[$index]}"
				if [[ ${aDRIVE_MOUNT_TARGET[$index]} =~ ^/(boot(/efi)?)?$ ]]; then

					# On RPi we need to use PARTUUID for Root/BootFS
					(( $G_HW_MODEL < 10 )) && dev_entry="PARTUUID=${aDRIVE_PART_UUID[$index]}"
					[[ ${aDRIVE_MOUNT_TARGET[$index]} == '/' ]] && options+=' 0 1' || options+=' 0 2' # dump + fsck flag

				else

					options+=',nofail,noauto,x-systemd.automount'

				fi

				# FS type: In case of unknown network drive, use "auto", to allow remove FS changes without breaking the fstab entry
				local type=${aDRIVE_FSTYPE[$index]}
				(( ${aDRIVE_ISNETWORKED[$index]} )) && type='auto'

				echo "$dev_entry ${aDRIVE_MOUNT_TARGET[$index]} ${type:-auto} noatime,lazytime$options" >> $fp_fstab_tmp

			fi

		done < <(df -a --output=source --exclude-type=tmpfs --exclude-type=ecryptfs --exclude-type=vboxsf --exclude-type=glusterfs | sed "\|^/dev/root$|c$G_ROOTFS_DEV" | mawk '/\// && !x[$0]++')

		# Check blkid for unmounted drives, if drive is not mounted, add entry as disabled/commented mount
		while read -r line
		do

			[[ $line ]] || continue

			# Exclude drives already found
			for i in "${aDRIVE_MOUNT_SOURCE[@]}"
			do
				[[ $i == "$line"* ]] && continue 2
			done

			# Must have a valid UUID! (this excludes /dev/mmcblk0)
			local uuid=$(blkid -s UUID -o value "$line")
			[[ $uuid ]] || continue

			G_DIETPI-NOTIFY 2 " - Detected unmounted drive: $line"

			Init_New_Device

			aDRIVE_UUID[$index]=$uuid
			aDRIVE_MOUNT_SOURCE[$index]=$line
			aDRIVE_MOUNT_TARGET[$index]="/mnt/${aDRIVE_UUID[$index]}"
			aDRIVE_SOURCE_DEVICE[$index]=$(Return_Drive_Without_Partitions "${aDRIVE_MOUNT_SOURCE[$index]}")
			[[ ${aDRIVE_MOUNT_SOURCE[$index]} == /dev/${aDRIVE_SOURCE_DEVICE[$index]} ]] || aDRIVE_ISPARTITIONTABLE[$index]=1
			(( ${aDRIVE_ISPARTITIONTABLE[$index]} )) && aDRIVE_PART_UUID[$index]=$(blkid -s PARTUUID -o value "${aDRIVE_MOUNT_SOURCE[$index]}")
			aDRIVE_FSTYPE[$index]=$(blkid -s TYPE -o value "${aDRIVE_MOUNT_SOURCE[$index]}")
			[[ ${aDRIVE_FSTYPE[$index]} ]] && aDRIVE_ISFILESYSTEM[$index]=1

			# FS type: In case of unknown network drive, use "auto", to allow remove FS changes without breaking the fstab entry
			local type=${aDRIVE_FSTYPE[$index]}
			(( ${aDRIVE_ISNETWORKED[$index]} )) && type='auto'

			echo "#UUID=${aDRIVE_UUID[$index]} ${aDRIVE_MOUNT_TARGET[$index]} ${type:-auto} noatime,lazytime,rw,nofail,noauto,x-systemd.automount" >> $fp_fstab_tmp

		done < <(blkid -o device)

		# Find unformated drives
		# - Exclude mtdblock devices: https://github.com/MichaIng/DietPi/issues/2067#issuecomment-422400520
		while read -r line
		do

			[[ $line ]] || continue

			# Exclude drives already found
			for i in "${aDRIVE_SOURCE_DEVICE[@]}"
			do
				[[ $line == $i* ]] && continue 2
			done

			G_DIETPI-NOTIFY 2 " - Detected unformated drive: /dev/$line"

			Init_New_Device

			aDRIVE_MOUNT_SOURCE[$index]="/dev/$line"
			aDRIVE_MOUNT_TARGET[$index]="/tmp/$line"
			aDRIVE_SOURCE_DEVICE[$index]=$line

		done < <(lsblk -nro NAME | sed '/^mtdblock[0-9]/d')

		# Set required global flags and deps for all drives found
		for i in "${!aDRIVE_MOUNT_SOURCE[@]}"
		do

			# Detect and set ROM drives
			[[ ${aDRIVE_MOUNT_SOURCE[$i]} == '/dev/sr'* ]] && aDRIVE_ISROM[$i]=1

			# Collect required APT packages for FS R/W access
			if [[ ${aDRIVE_FSTYPE[$i]} == 'ntfs' ]]; then

				local need_ntfs='ntfs-3g'

			elif [[ ${aDRIVE_FSTYPE[$i]} =~ 'hfs' ]]; then

				local need_hfs='hfsplus'

			elif [[ ${aDRIVE_FSTYPE[$i]} == 'exfat' ]]; then

				local need_exfat='exfat-fuse'

			fi

		done

		# Debug drive detection, exit after first init
		if [[ $G_DEBUG == 1 ]]; then

			G_DIETPI-NOTIFY 0 'DEBUG INFO:'
			for i in "${!aDRIVE_MOUNT_SOURCE[@]}"
			do
				G_DIETPI-NOTIFY 0 "Index=$i
aDRIVE_UUID ${aDRIVE_UUID[$i]}
aDRIVE_PART_UUID ${aDRIVE_PART_UUID[$i]}
aDRIVE_MOUNT_SOURCE ${aDRIVE_MOUNT_SOURCE[$i]}
aDRIVE_MOUNT_TARGET ${aDRIVE_MOUNT_TARGET[$i]}
aDRIVE_SOURCE_DEVICE ${aDRIVE_SOURCE_DEVICE[$i]}
aDRIVE_FSTYPE ${aDRIVE_FSTYPE[$i]}
aDRIVE_SIZE_TOTAL ${aDRIVE_SIZE_TOTAL[$i]}
aDRIVE_SIZE_USED ${aDRIVE_SIZE_USED[$i]}
aDRIVE_SIZE_PERCENTUSED ${aDRIVE_SIZE_PERCENTUSED[$i]}
aDRIVE_ISFILESYSTEM ${aDRIVE_ISFILESYSTEM[$i]}
aDRIVE_ISMOUNTED ${aDRIVE_ISMOUNTED[$i]}
aDRIVE_ISREADONLY_CURRENTLY ${aDRIVE_ISREADONLY_CURRENTLY[$i]}
aDRIVE_ISNETWORKED ${aDRIVE_ISNETWORKED[$i]}
aDRIVE_ISROM ${aDRIVE_ISROM[$i]}
aDRIVE_ISPARTITIONTABLE ${aDRIVE_ISPARTITIONTABLE[$i]}
"
			done
			exit

		fi

		# Remove x-systemd.automount if not supported by kernel: https://github.com/MichaIng/DietPi/issues/1607#issuecomment-372030565
		if ! modprobe autofs4 --dry-run &> /dev/null; then

			sed -Ei '/x-systemd\.automount/s/,(noauto|x-systemd\.automount)//g' $fp_fstab_tmp
			G_DIETPI-NOTIFY 2 'autofs4 module not available in kernel, x-systemd.automount has been disabled, all drives will be mounted at boot instead'

		fi

		Update_Menu_Drive_Index

		# Move new fstab in place and reload systemd generators
		cp -a $fp_fstab_tmp /etc/fstab
		systemctl daemon-reload
		rm $fp_fstab_tmp

		# Install required APT packages for FS R/W access
		[[ $APT_CHECK == 0 && $need_ntfs$need_hfs$need_exfat ]] && G_AG_CHECK_INSTALL_PREREQ $need_ntfs $need_hfs $need_exfat && APT_CHECK=1

		sync
		mount -a

	}

	Update_Menu_Drive_Index(){

		local i
		[[ $MENU_DRIVE_TARGET ]] && for i in "${!aDRIVE_MOUNT_TARGET[@]}"
		do
			[[ $MENU_DRIVE_TARGET == "${aDRIVE_MOUNT_TARGET[$i]}" ]] || continue
			MENU_DRIVE_INDEX=$i
			[[ $G_DEBUG == 1 ]] && echo "[DEBUG$i] EDIT | ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} | ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]} | ${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}"
			break
		done

	}

	# $1=source
	Return_Drive_Without_Partitions(){

		# IDE/SATA/SCSI
		if [[ $1 == /dev/[sh]d[a-z]* ]]; then

			local drive=${1#/dev/}
			echo "${drive%[0-9]}"

		# MMC/NVMe
		elif [[ $1 == '/dev/mmcblk'* || $1 == '/dev/nvme'* ]]; then

			local drive=${1#/dev/}
			echo "${drive%p[0-9]}"

		# Network drive
		else

			echo "$1"

		fi

	}

	# $1=source $2=target
	Mount_Drive(){

		local source=$1
		local target=$2

		G_WHIP_DEFAULT_ITEM=$target
		if G_WHIP_INPUTBOX "Please enter the desired mount point.\n - Default and recommended = $target\n
NB: The path must start with /mnt/ and be unique. Spaces will be converted automatically to underscores (_)."; then

			if [[ $G_WHIP_RETURNED_VALUE == '/mnt/'* ]]; then

				# Replace spaces with underscores
				target=${G_WHIP_RETURNED_VALUE//[[:space:]]/_}

			else

				G_WHIP_MSG "Invalid mount target location:\n - $G_WHIP_RETURNED_VALUE\n\nThe drive will now be mounted to:\n - $target"

			fi

		fi

		if [[ -d $target ]]; then

			if [[ $(ls -A "$target") ]]; then

				G_WHIP_MSG "[FAILED]:\n\nThe mount target directory already exists, and, contains data:\n - $target\n\nPlease retry, using a unique mount target location."
				return 1

			else

				G_WHIP_YESNO "[WARNING]:\n\nThe mount target directory already exists, however, it does not contain any files or data at this time:\n - $target\n
Do you wish to ignore this warning, and, mount the drive regardless?" || return 1

			fi

		fi

		# Mount options
		local aoptions=()
		# - FS type specific options
		local fs_type=$(blkid "$source" -s TYPE -o value)
		if [[ $fs_type == 'ntfs' ]]; then

			aoptions=('-o' 'permissions')

		fi

		G_EXEC_NOHALT=1 G_EXEC mkdir -p "$target" || return 1
		G_EXEC_NOHALT=1 G_EXEC mount "$source" "$target" "${aoptions[@]}" || return 1
		MENU_DRIVE_TARGET=$target
		Init_Drives_and_Refresh

	}

	# $1=target
	Unmount_Drive(){

		local target=$1

		if G_EXEC_NOHALT=1 G_EXEC umount "$target"; then

			sed -i "\#[[:blank:]]${target}[[:blank:]]#d" /etc/fstab # Only needed for networked drives currently...
			rmdir "$target"
			Init_Drives_and_Refresh

		fi

	}

	Resize_FS(){

		if [[ ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} == '/' ]]; then

			systemctl unmask dietpi-fs_partition_resize
			G_EXEC systemctl enable dietpi-fs_partition_resize
			G_WHIP_YESNO 'RootFS resize will occur on next reboot.\n\nWould you like to reboot the system now?' && reboot

		else

			G_EXEC_NOHALT=1 G_EXEC resize2fs "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"
			Init_Drives_and_Refresh

		fi

	}

	Run_Format(){

		local i text_desc info_format_fs_type
		local info_format_type_output='Drive format' # Used in complete message

		# No partition table, force drive wipe
		(( ${aDRIVE_ISPARTITIONTABLE[$MENU_DRIVE_INDEX]} )) || FORMAT_MODE=0

		if (( $FORMAT_MODE )); then

			text_desc=" - ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}\n - UUID=${aDRIVE_UUID[$MENU_DRIVE_INDEX]}\n - Filesystem type: $format_type_text\n\nALL DATA on this PARTITION will be DELETED.\nDo you wish to continue?"

		else

			text_desc=" - /dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}\n - UUID=${aDRIVE_UUID[$MENU_DRIVE_INDEX]}\n - Partition table: $partition_table_text\n - Filesystem type: $format_type_text\n\nALL DATA and PARTITIONS on this drive will be DELETED.\nDo you wish to continue?"

		fi

		if G_WHIP_YESNO "Ready to format:\n$text_desc"; then

			# Partition format
			if (( $FORMAT_MODE )); then

				info_format_type_output='Single partition format'

				# Unmount
				umount "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

				# Clear partition from device
				G_DIETPI-NOTIFY 2 "Writing zeros to partition ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"
				G_EXEC dd if=/dev/zero of="${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}" bs=4K count=1337

			# Drive format: Create a new partition table
			else

				# Umount and zero all partitions on device
				for i in "/dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}"?*
				do

					[[ -b $i ]] || continue
					umount "$i"
					G_DIETPI-NOTIFY 2 "Writing zeros to partition: $i"
					G_EXEC dd if=/dev/zero of="$i" bs=4K count=10 # Partition wipe must be done 1st, else blkid still reports UUIDs etc

				done

				# Unmount whole drive in case of fs on drive without partition table
				umount "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

				# Clear partition table from device
				G_DIETPI-NOTIFY 2 "Writing zeros to partition table: /dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}"
				G_EXEC dd if=/dev/zero of="/dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}" bs=4K count=1337 # Block device wipe

				# Create partition table type
				local parition_table_type='gpt'
				(( $FORMAT_GPT )) || parition_table_type='msdos'

				G_DIETPI-NOTIFY 2 "Creating partition table, with target type: $parition_table_type"
				G_EXEC parted -s "/dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}" mklabel $parition_table_type
				G_EXEC parted -s "/dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}" mkpart primary 0% 100%
				partprobe "/dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}"

				sleep 1 # Due to systemd automount, wait for it
				umount "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

				# Generate new aDRIVE_MOUNT_SOURCE location to use
				# - mmcblkXp1/nvmeXn1p1
				aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]="/dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}p1"
				# - h/sdX1
				if [[ ${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]} == [sh]d[a-z]* ]]; then

					aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]="/dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}1"

				# - NanoPC-T4 + NVMe special: https://github.com/MichaIng/DietPi/issues/2102#issue-366001513
				elif [[ $G_HW_MODEL == 68 && ${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]} == nvme* ]]; then

					aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]="/dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}p6"

				fi

			fi

			# Format ext4
			if (( $FORMAT_FILESYSTEM_TYPE == 0 )); then

				# force
				info_format_fs_type='EXT4'
				G_EXEC mkfs.ext4 -F -m 0 "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"
				resize2fs "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

			# Format NTFS
			elif (( $FORMAT_FILESYSTEM_TYPE == 1 )); then

				# fast format / no indexing / force
				info_format_fs_type='NTFS'
				G_EXEC mkfs.ntfs -f -I -F "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

			# Format FAT32
			elif (( $FORMAT_FILESYSTEM_TYPE == 2 )); then

				# Use 1 parition on whole device
				info_format_fs_type='FAT'
				G_EXEC mkfs.vfat -I "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

			# Format HFS+
			elif (( $FORMAT_FILESYSTEM_TYPE == 3 )); then

				info_format_fs_type='HFS+'
				G_EXEC mkfs.hfsplus "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

			# Format btrfs
			elif (( $FORMAT_FILESYSTEM_TYPE == 4 )); then

				# force
				info_format_fs_type='BTRFS'
				G_EXEC mkfs.btrfs -f "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

			# Format f2fs
			elif (( $FORMAT_FILESYSTEM_TYPE == 5 )); then

				info_format_fs_type='F2FS'
				G_EXEC mkfs.f2fs "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

			# Format exFAT
			elif (( $FORMAT_FILESYSTEM_TYPE == 6 )); then

				info_format_fs_type='exFAT'
				G_EXEC mkfs.exfat "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

			fi

			G_DIETPI-NOTIFY 0 "Created $info_format_fs_type filesystem: ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"
			FORMAT_COMPLETED=1

			# Remove old mount point
			[[ -e ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} ]] && rm -R "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}"

			# Automatically mount it
			local new_uuid=$(blkid -s UUID -o value "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}")
			if ! Mount_Drive "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}" "/mnt/$new_uuid"; then

				MENU_DRIVE_TARGET="/mnt/$new_uuid"
				Init_Drives_and_Refresh

			fi

			G_WHIP_MSG "[  OK  ] Format completed\n
 - Format Type      : $info_format_type_output
 - Filesystem Type  : $info_format_fs_type
 - Mount Source     : ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}
 - Mount Target     : $MENU_DRIVE_TARGET
 - UUID             : $new_uuid"

		fi

	}

	RootFS_Move(){

		(( $SERVICES_STOPPED )) || { /boot/dietpi/dietpi-services stop; SERVICES_STOPPED=1; }

		# Install rsync
		G_AG_CHECK_INSTALL_PREREQ rsync

		# Disable swap
		/boot/dietpi/func/dietpi-set_swapfile 0

		# Create new fstab
		G_EXEC cp -a /etc/fstab /etc/fstab_new
		# - Remove automatic entry for new UUID
		G_EXEC sed -i "\@[[:blank:]]${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}[[:blank:]]@d" /etc/fstab_new
		# - Replace old with new rootfs entry
		local dev_entry="UUID=${aDRIVE_UUID[$MENU_DRIVE_INDEX]}"
		(( $G_HW_MODEL < 10 )) && dev_entry="PARTUUID=${aDRIVE_PART_UUID[$MENU_DRIVE_INDEX]}"
		G_EXEC sed -i "\@UUID=[^[:blank:]]*[[:blank:]]\+/[[:blank:]]@c$dev_entry / ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} noatime,lazytime,rw 0 1" /etc/fstab_new

		# Mount rootfs to tmp mountpoint to allow rsync
		# - rsync "-x" option prevents copying mounts content, but it copies permissions of mountpoint dirs according to mount options instead of those of the dir on the parent fs.
		# - Since mount permissions might not be wanted for the underlying file system dir, we copy from a temporary mountpoint to assure that underlying rootfs content matches 100%.
		G_EXEC mkdir -p /tmp/tmp_rootfs
		G_EXEC mount "$G_ROOTFS_DEV" /tmp/tmp_rootfs

		# Start rsync
		if ! G_EXEC_NOEXIT=1 G_EXEC rsync -aHv --delete /tmp/tmp_rootfs/ "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}/"; then

			G_DIETPI-NOTIFY 1 'Rsync has failed, RootFS transfer has been aborted.'
			rm /etc/fstab_new
			umount /tmp/tmp_rootfs
			rmdir /tmp/tmp_rootfs
			return 1

		fi

		# Remove volatile systemd service PrivateTmp dirs in /var/tmp and target drive mountpoint dir
		rm -Rf "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}"/var/tmp/systemd-private-*
		rmdir "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}"

		# Move new fstab in place
		G_EXEC mv /etc/fstab_new "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}/etc/fstab"

		# Recreate swap
		#/boot/dietpi/func/dietpi-set_swapfile 1 "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}/var/swap"
		# - Manually update location
		#G_CONFIG_INJECT 'AUTO_SETUP_SWAPFILE_LOCATION=' 'AUTO_SETUP_SWAPFILE_LOCATION=/var/swap' /boot/dietpi.txt

		# RPi: /boot/cmdline.txt
		if (( $G_HW_MODEL < 10 )); then

			# Find current root= and replace
			local rootfs_current=$(mawk '{for(i=1;i<=NF;i++) {print $i} }' /boot/cmdline.txt | grep -m1 '^root=')
			G_EXEC sed -i "s#$rootfs_current#root=PARTUUID=${aDRIVE_PART_UUID[$MENU_DRIVE_INDEX]}#g" /boot/cmdline.txt

			# Set FS type
			local rootfstype_current=$(mawk '{for(i=1;i<=NF;i++) {print $i} }' /boot/cmdline.txt | grep -m1 '^rootfstype=')
			G_EXEC sed -i "s#$rootfstype_current#rootfstype=${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]}#g" /boot/cmdline.txt

			# Add root delay
			grep -qE '(^|[[:blank:]])rootdelay=' /boot/cmdline.txt || G_EXEC sed -i "s#rootfstype=${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]}#rootfstype=${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} rootdelay=10#" /boot/cmdline.txt

		# C2/XU4: /boot/boot.ini
		elif [[ $G_HW_MODEL == 1[12] ]]; then

			# Find current root= to replace
			local rootfs_current=$(mawk '{for(i=1;i<=NF;i++) {print $i} }' /boot/boot.ini | grep -m1 '^root=' | sed 's/\"//')
			G_EXEC sed -i "s#$rootfs_current#root=UUID=${aDRIVE_UUID[$MENU_DRIVE_INDEX]}#g" /boot/boot.ini

		fi

		systemctl daemon-reload
		sync

		G_WHIP_MSG 'RootFS transfer has successfully completed.\n\nA reboot is required, please press <return> to reboot now.'
		reboot

	}

	Toggle_WriteMode(){

		local exit_status=0
		local message_result=0

		if (( ${aDRIVE_ISREADONLY_CURRENTLY[$MENU_DRIVE_INDEX]} )); then

			message_result=$(mount -v -o rw,remount "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}" 2>&1)
			exit_status=$?

		else

			(( $SERVICES_STOPPED )) || { /boot/dietpi/dietpi-services stop; SERVICES_STOPPED=1; }

			# RootFS, set fstab now, else, will not be applied to /etc/fstab during Init as already RO: https://github.com/MichaIng/DietPi/issues/2604
			if [[ ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} == '/' ]]; then

				local line_number=$(grep -n "[[:blank:]]${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}[[:blank:]].*,rw" /etc/fstab | cut -d : -f 1)
				sed -i "${line_number}s/,rw/,ro/" /etc/fstab

			fi
			message_result=$(mount -v -o ro,remount "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}" 2>&1)
			exit_status=$?

		fi

		(( $exit_status )) && G_WHIP_MSG "[FAILED] Could not apply\n\nError log:\n - $message_result"
		Init_Drives_and_Refresh

	}

	TARGETMENUID=0
	MENU_LASTITEM=

	Menu_Main(){

		# Generate menu
		G_WHIP_MENU_ARRAY=()

		# - Create a nice category list, to match items to their block device (eg: mmcblk0)
		local acategory_list=()
		local i j
		for i in "${!aDRIVE_MOUNT_SOURCE[@]}"
		do

			local new_cat=1

			for j in "${!acategory_list[@]}"
			do

				if [[ ${aDRIVE_SOURCE_DEVICE[$i]} == "${acategory_list[$j]}" ]]; then

					new_cat=0
					break

				fi

			done

			# Add
			(( $new_cat )) && acategory_list+=("${aDRIVE_SOURCE_DEVICE[$i]}")

		done

		# List all available drives, if no drive found, list info for user.
		local drive_available=0
		for i in "${!acategory_list[@]}"
		do

			drive_available=1
			G_WHIP_MENU_ARRAY+=('' "●─ ${acategory_list[$i]} ")

			for j in "${!aDRIVE_MOUNT_SOURCE[@]}"
			do

				if [[ ${aDRIVE_SOURCE_DEVICE[$j]} == "${acategory_list[$i]}" ]]; then

					local string_fstype=${aDRIVE_FSTYPE[$j]}
					(( ${aDRIVE_ISNETWORKED[$j]} )) && string_fstype='Net'

					# Drive is fully mounted:
					if (( ${aDRIVE_ISMOUNTED[$j]} )); then

						G_WHIP_MENU_ARRAY+=("${aDRIVE_MOUNT_TARGET[$j]}" ": ${aDRIVE_MOUNT_SOURCE[$j]} | $string_fstype | Capacity: ${aDRIVE_SIZE_TOTAL[$j]} | Used: ${aDRIVE_SIZE_USED[$j]} (${aDRIVE_SIZE_PERCENTUSED[$j]})")

					# Drive has no FS:
					elif (( ! ${aDRIVE_ISFILESYSTEM[$j]} )); then

						# ROM device with no ROM attached
						if (( ${aDRIVE_ISROM[$j]} )); then

							G_WHIP_MENU_ARRAY+=("${aDRIVE_MOUNT_TARGET[$j]}" ": ${aDRIVE_MOUNT_SOURCE[$j]} | Please insert media into the ROM device")

						else

							G_WHIP_MENU_ARRAY+=("${aDRIVE_MOUNT_TARGET[$j]}" ": ${aDRIVE_MOUNT_SOURCE[$j]} | No filesystem / format required")

						fi

					# Drive is not mounted:
					else

						G_WHIP_MENU_ARRAY+=("${aDRIVE_MOUNT_TARGET[$j]}" ": ${aDRIVE_MOUNT_SOURCE[$j]} | $string_fstype | Not mounted")

					fi

				fi

			done

		done

		unset acategory_list

		G_WHIP_MENU_ARRAY+=('' '●─ Global Options ')
		G_WHIP_MENU_ARRAY+=('Idle Spindown' ': Set a global idle duration, before drives power down')
		G_WHIP_MENU_ARRAY+=('' '●─ Add / Refresh Drives ')
		G_WHIP_MENU_ARRAY+=('Add network drive' ': Select to mount networked drives')
		if (( $drive_available )); then

			G_WHIP_MENU_ARRAY+=('Refresh' ': Scan for recently added/removed drives')

		else

			G_WHIP_MENU_ARRAY+=('Refresh' ': No drives found. Insert a drive and select this option')

		fi

		# User data
		local userdata_location_text="RootFS ($FP_USERDATA_CURRENT)"
		[[ $FP_USERDATA_CURRENT == '/mnt/dietpi_userdata' ]] || userdata_location_text=$FP_USERDATA_CURRENT

		G_WHIP_DEFAULT_ITEM=$MENU_LASTITEM
		G_WHIP_BUTTON_CANCEL_TEXT='Exit'
		if G_WHIP_MENU "Please select a drive to see available options.\n - User data location: $userdata_location_text"; then

			MENU_LASTITEM=$G_WHIP_RETURNED_VALUE

			# Refresh
			if [[ $G_WHIP_RETURNED_VALUE == 'Refresh' ]]; then

				Init_Drives_and_Refresh

			elif [[ $G_WHIP_RETURNED_VALUE == 'Add network drive' ]]; then

				TARGETMENUID=3

			elif [[ $G_WHIP_RETURNED_VALUE == 'Idle Spindown' ]]; then

				local apm=127 # Hardcode to highest value that still allows spin-down
				local current_spindown=
				[[ -f '/etc/hdparm.conf' ]] && current_spindown=$(mawk '/spindown_time/{print $3;exit}' /etc/hdparm.conf)
				disable_error=1 G_CHECK_VALIDINT "$current_spindown" 0 251 && G_WHIP_DEFAULT_ITEM=$current_spindown || G_WHIP_DEFAULT_ITEM=241


				G_WHIP_MENU_ARRAY=('0' ': Disabled')

				local minutes seconds text
				for i in {12..251}
				do

					if (( $i < 241 )); then

						minutes=$(( $i * 5 / 60 ))
						seconds=$(( $i * 5 % 60 ))

						text="$minutes Minute"
						(( $minutes > 1 )) && text+='s'
						(( $seconds )) && text+=", $seconds Seconds"

					else

						text=" $(( ( $i - 240 ) * 30 )) Minutes"

					fi

					G_WHIP_MENU_ARRAY+=("$i" ": $text")

				done

				if G_WHIP_MENU 'Please select an idle duration of time, before each drive is powered down:
 - This will be applied to all drives on the system
 - Not all drives support the feature of "hdparm" and visa versa. End results may vary.
 - You can check status with "hdparm -C [sh]d[a-z]"'; then

					# Since Debian Bullseye, spindown_time is not applied if APM is not supported by the drive. force_spindown_time is required to override that.
					local spindown_setting='spindown_time'
					(( $G_DISTRO > 5 )) && spindown_setting='force_spindown_time'

					G_DIETPI-NOTIFY 2 'Applying spindown timeout to all drives now...'
					hdparm -B $apm -S "$G_WHIP_RETURNED_VALUE" /dev/[sh]d[a-z]

					G_DIETPI-NOTIFY 2 'Applying spindown timeout to /etc/hdparm.conf to be effective from next boot on...'
					echo -e "apm = $apm\n$spindown_setting = $G_WHIP_RETURNED_VALUE" > /etc/hdparm.conf

				fi

			# Edit drive
			elif [[ $G_WHIP_RETURNED_VALUE ]]; then

				TARGETMENUID=1
				MENU_DRIVE_TARGET=$G_WHIP_RETURNED_VALUE
				Update_Menu_Drive_Index

			fi

		else

			Menu_Exit

		fi

	}

	Notification(){

		if (( $1 == 0 )); then

			G_WHIP_MSG "[FAILED]\n\nYour DietPi userdata is currently located on this drive:\n - $FP_USERDATA_CURRENT\n\nThe requested option for this drive is not currently possible.\n\nPlease move your userdata elsewhere, before trying again:\nhttps://dietpi.com/docs/dietpi_tools/#quick-selections"

		elif (( $1 == 1 )); then

			G_WHIP_MSG "[FAILED]\n\nThe DietPi swapfile is currently located on this drive:\n - $FP_SWAPFILE_CURRENT\n\nThe requested option for this drive is not currently possible.\n\nPlease move the swapfile elsewhere, before trying again."

		fi

	}

	Menu_Drive(){

		G_WHIP_MENU_ARRAY=()
		local partition_contains_userdata=0
		local partition_contains_swapfile=0
		local whiptail_desc="Mount target: ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}"
		whiptail_desc+="\nMount source: ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

		# No filesystem
		if (( ! ${aDRIVE_ISFILESYSTEM[$MENU_DRIVE_INDEX]} )); then

			if (( ${aDRIVE_ISROM[$MENU_DRIVE_INDEX]} )); then

				whiptail_desc+='\nStatus:       No media found, please insert media into the ROM device'
				G_WHIP_MENU_ARRAY+=('Refresh' ': No media found, please insert media into the ROM device')

			else

				whiptail_desc+='\nStatus:       Drive has no known filesystem and must be formatted'
				G_WHIP_MENU_ARRAY+=('Format' ': Create a filesystem for this drive/partition')

			fi

		# Filesystem
		else

			whiptail_desc+="\nFilesystem:   ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]}"
			[[ ${aDRIVE_UUID[$MENU_DRIVE_INDEX]} ]] && whiptail_desc+="\nUUID:         ${aDRIVE_UUID[$MENU_DRIVE_INDEX]}"

			if (( ${aDRIVE_ISMOUNTED[$MENU_DRIVE_INDEX]} )); then

				whiptail_desc+="\nAllocation:   Capacity: ${aDRIVE_SIZE_TOTAL[$MENU_DRIVE_INDEX]}iB | Used: ${aDRIVE_SIZE_USED[$MENU_DRIVE_INDEX]}iB (${aDRIVE_SIZE_PERCENTUSED[$MENU_DRIVE_INDEX]})\nStatus:       Drive is online and ready for use"

				# Unmount
				# - Disable mount control for /boot /rootfs
				if [[ ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} != '/' && ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} != '/boot' ]]; then

					G_WHIP_MENU_ARRAY+=('' '●─ Mount Control ')
					if (( ${aDRIVE_ISNETWORKED[$MENU_DRIVE_INDEX]} )); then

						G_WHIP_MENU_ARRAY+=('Remove' ': Unmount networked drive and remove it from database')

					else

						G_WHIP_MENU_ARRAY+=('Unmount' ': Allows you to physically remove the drive')

					fi

				fi

				if (( ! ${aDRIVE_ISROM[$MENU_DRIVE_INDEX]} )); then

					if (( ! ${aDRIVE_ISREADONLY_CURRENTLY[$MENU_DRIVE_INDEX]} )); then

						G_WHIP_MENU_ARRAY+=('' '●─ Benchmark Options ')
						G_WHIP_MENU_ARRAY+=('Benchmark' ': Test read and write speeds')

						# Userdata location/swapfile
						if (( ! ${aDRIVE_ISNETWORKED[$MENU_DRIVE_INDEX]} )); then

							G_WHIP_MENU_ARRAY+=('' '●─ Userdata & Swap options ')

							if [[ ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} != '/' && $FP_USERDATA_CURRENT == ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}* ]] ||
								[[ ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} == '/' && $FP_USERDATA_CURRENT == '/mnt/dietpi_userdata' ]]; then

								partition_contains_userdata=1
								G_WHIP_MENU_ARRAY+=('User data' ": [X] | DietPi user data is currently located on this drive")

							else

								G_WHIP_MENU_ARRAY+=('User data' ': [ ] | Select to transfer DietPi user data to this drive')

							fi

							# Swapfile
							# - NB: / rootfs will always be detected in this check, however, no rootFS options for umount and format...
							local swapfile_size=$(sed -n '/^[[:blank:]]*AUTO_SETUP_SWAPFILE_SIZE=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
							if (( $swapfile_size > 0 )) &&
								{ [[ $FP_SWAPFILE_CURRENT == ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}* && ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} != '/' ]] ||
								[[ $FP_SWAPFILE_CURRENT == '/var/swap' && ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} == '/' ]]; }; then

								partition_contains_swapfile=1
								G_WHIP_MENU_ARRAY+=('Swapfile' ": [X] | ${swapfile_size} MiB used on this drive, select to change size")

							else

								G_WHIP_MENU_ARRAY+=('Swapfile' ': [ ] | Select to transfer swapfile to this drive')

							fi

						fi

					fi

					if (( ! ${aDRIVE_ISNETWORKED[$MENU_DRIVE_INDEX]} )); then

						G_WHIP_MENU_ARRAY+=('' '●─ Advanced options ')

						# Read only?
						local read_only_state='[ ]'
						local read_only_state_text='Disabled'
						if (( ${aDRIVE_ISREADONLY_CURRENTLY[$MENU_DRIVE_INDEX]} )); then

							read_only_state='[X]'
							read_only_state_text='Enabled'

						fi
						G_WHIP_MENU_ARRAY+=('Read Only' ": $read_only_state | Select to toggle RW/RO modes")
						whiptail_desc+="\nRead only:    $read_only_state_text"

						# Show reserved blocks percentage for ext4 drives behind capacity:
						if [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} == 'ext4' ]]; then

							local fp_tmp='.dumpe2fs_out_tmp' reserved_blocks_percent_current=0
							dumpe2fs -h "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}" > $fp_tmp
							local block_count=$(mawk '/^Block count:/{print $3;exit}' $fp_tmp)
							local reserved_block_count=$(mawk '/^Reserved block count:/{print $4;exit}' $fp_tmp)
							rm $fp_tmp
							reserved_blocks_percent_current=$(printf '%0.2f' "$(($reserved_block_count*100000/$block_count+1))e-3")
							G_WHIP_MENU_ARRAY+=('Reserved blocks' ": [$reserved_blocks_percent_current%] | Adjust percentage of reserved blocks on this drive")

						fi

					fi

				fi

			else

				whiptail_desc+='\nStatus:       Drive is not mounted and can be unplugged'
				G_WHIP_MENU_ARRAY+=('' '●─ Mount Control ')
				G_WHIP_MENU_ARRAY+=('Mount' ": Mount the drive to ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}")
				G_WHIP_MENU_ARRAY+=('' '●─ Advanced Options ')

			fi

			if ! (( ${aDRIVE_ISROM[$MENU_DRIVE_INDEX]} || ${aDRIVE_ISNETWORKED[$MENU_DRIVE_INDEX]} )); then

				# fsck
				G_WHIP_MENU_ARRAY+=('Check & Repair' ': Check and optionally repair filesystem')

				# Resize
				if [[ ${aDRIVE_ISREADONLY_CURRENTLY[$MENU_DRIVE_INDEX]} == 0 && ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} == 'ext4' ]]; then

					G_WHIP_MENU_ARRAY+=('Resize' ': Maximize the available filesystem size')

				fi

				# Transfer RootFS
				if [[ ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]} != "$G_ROOTFS_DEV" ]] && (( $G_HW_MODEL < 13 )); then

					G_WHIP_MENU_ARRAY+=('Transfer RootFS' ': Transfer RootFS to this drive')

				fi

				# Disable format for /boot /rootfs
				if [[ ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} != '/' && ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} != '/boot' ]]; then

					G_WHIP_MENU_ARRAY+=('Format' ': Select to see formatting options')

				fi

			fi

		fi

		# I/O scheduler
		local io_schedulers="/sys/block/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}/queue/scheduler"
		if [[ -e $io_schedulers ]]; then

			local aio_schedulers=()
			for i in $(<"$io_schedulers")
			do

				if [[ $i == '['*']' ]]; then

					i=${i#[}; i=${i%]}
					local io_scheduler_current=$i
				fi
				aio_schedulers+=("$i" '')

			done
			[[ ${aio_schedulers[0]} ]] && G_WHIP_MENU_ARRAY+=('I/O Scheduler' ": [${io_scheduler_current:-N/A}]")

		fi

		G_WHIP_BUTTON_CANCEL_TEXT='Back'
		G_WHIP_DEFAULT_ITEM=$MENU_LASTITEM
		if G_WHIP_MENU "$whiptail_desc"; then

			MENU_LASTITEM=$G_WHIP_RETURNED_VALUE

			if [[ $G_WHIP_RETURNED_VALUE == 'Mount' ]]; then

				Mount_Drive "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}" "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}"

			elif [[ $G_WHIP_RETURNED_VALUE == 'Unmount' || $G_WHIP_RETURNED_VALUE == 'Remove' ]]; then

				# Disallow if userdata is located on this drive!
				if (( $partition_contains_userdata )); then

					Notification 0

				elif (( $partition_contains_swapfile )); then

					Notification 1

				elif [[ $G_WHIP_RETURNED_VALUE == 'Remove' ]]; then

					if G_WHIP_YESNO "Do you wish to unmount and remove the following networked drive from this system?
 - ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]} > ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}
\nNB: You can add additional network shares at a later date through the 'dietpi-drive_manager' main menu."; then

						Unmount_Drive "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}"
						TARGETMENUID=0

					fi

				else

					Unmount_Drive "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}"

				fi

			elif [[ $G_WHIP_RETURNED_VALUE == 'Swapfile' ]]; then

				local min=0
				local current_freespace=$(G_CHECK_FREESPACE "${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}")
				local max=$(( $current_freespace - 2000 ))
				if [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} == 'btrfs' ]]; then

					G_WHIP_MSG '[FAILED] The file system BTRFS does not support swap files.\n
Please choose another drive or format this one with another file system, e.g. ext4.'

				elif (( $max < $min )); then

					G_WHIP_MSG "[FAILED] Insufficent free space on ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}, a minimum of 2GB is required ($current_freespace MiB available)."

				else

					G_WHIP_DEFAULT_ITEM=$swapfile_size
					if G_WHIP_INPUTBOX "Please input a new value for swapfile size (MiB):
\nSwapfile has a swapiness setting of 1, and, is used only to prevent out of memory errors.
\n - Recommended value = 1 (auto)\n - 0 = Disable swapfile\n - 1 = Auto size swapfile (2GB - RAM = size)\n - 2 - $max = Manual size"; then

						if G_CHECK_VALIDINT "$G_WHIP_RETURNED_VALUE" $min $max; then

							local fp_target_swapfile="${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}/.swapfile"
							[[ ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} == '/' ]] && fp_target_swapfile='/var/swap'

							(( $SERVICES_STOPPED )) || { /boot/dietpi/dietpi-services stop; SERVICES_STOPPED=1; }
							/boot/dietpi/func/dietpi-set_swapfile "$G_WHIP_RETURNED_VALUE" "$fp_target_swapfile"
							FP_SWAPFILE_CURRENT=$fp_target_swapfile

						fi

					fi

				fi

			elif [[ $G_WHIP_RETURNED_VALUE == 'Reserved blocks' ]]; then

				while :
				do

					G_WHIP_DEFAULT_ITEM=$reserved_blocks_percent_current
					G_WHIP_INPUTBOX 'Ext4 formatted drives allow the reservation of drive space for the root user to assure system functionality, if filled by other users or processes, and to avoid fragmentation of large files.\n
However, on modern drives, the default of 5% reserved blocks is often by orders of magnitude larger than necessary. You may want to reduce the percentage to an absolute reserved space of about 500 MiB, which should be enough, to enable root user starting and maintaining the system. Additionally, on non rootfs drives reserved blocks are not necessary at all.\n
Please enter the desired percentage of reserved blocks, e.g. "0.05" for 0.05% or "10" for 10%.\nNote: Only values between "0" and "50" are allowed.' || break

					if [[ $G_WHIP_RETURNED_VALUE =~ ^[0-9]*\.?[0-9]*$ && $G_WHIP_RETURNED_VALUE != '.' && $(mawk '$1 <= 50' <<< "$G_WHIP_RETURNED_VALUE") ]]; then

						G_EXEC tune2fs -m "$G_WHIP_RETURNED_VALUE" "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"
						break

					else

						G_WHIP_MSG 'Error: Allowed are only integers or floats between "0" and "50", e.g. "10" or "0.5".\n\nPlease try again...'

					fi

				done

			elif [[ $G_WHIP_RETURNED_VALUE == 'User data' ]]; then

				local fp_target_userdata_dir=${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]}
				# Assign location if under RootFS
				[[ ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} == '/' ]] && fp_target_userdata_dir='/mnt'

				fp_target_userdata_dir+='/dietpi_userdata'

				if [[ $fp_target_userdata_dir != "$FP_USERDATA_CURRENT" ]]; then

					if G_WHIP_YESNO "Your user data will be moved:\n - From: $FP_USERDATA_CURRENT\n - To: $fp_target_userdata_dir\n\nDo you wish to continue?"; then

						(( $SERVICES_STOPPED )) || { /boot/dietpi/dietpi-services stop; SERVICES_STOPPED=1; }
						export G_DIETPI_SERVICES_DISABLE=1
						/boot/dietpi/func/dietpi-set_userdata "$FP_USERDATA_CURRENT" "$fp_target_userdata_dir" && FP_USERDATA_CURRENT=$fp_target_userdata_dir
						unset G_DIETPI_SERVICES_DISABLE
						sleep 1

					fi

				fi

			elif [[ $G_WHIP_RETURNED_VALUE == 'Format' ]]; then

				# Disallow if userdata is located on this drive!
				if (( $partition_contains_userdata )); then

					Notification 0

				elif (( $partition_contains_swapfile )); then

					Notification 1

				# User must unmount partition on this drive, before we can format, this ensures a valid unmount check
				elif (( ${aDRIVE_ISMOUNTED[$MENU_DRIVE_INDEX]} )); then

					G_WHIP_MSG "The partition (${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}) must be unmounted, before formatting is allowed.\n\nPlease use the option 'Unmount' on the next screen, then retry the 'Format' option."

				else

					TARGETMENUID=2

				fi

			# Resize
			elif [[ $G_WHIP_RETURNED_VALUE == 'Resize' ]]; then

				Resize_FS

			# Transfer RootFS
			elif [[ $G_WHIP_RETURNED_VALUE == 'Transfer RootFS' ]]; then

				# Kernel lacks native BTRFS support (enabled as module): https://github.com/MichaIng/DietPi/issues/2909
				if [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} == 'btrfs' ]]; then

					G_WHIP_MSG "[WARNING] Booting from ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} is not supported by this device.\n
We recommend \"ext4\" as filesystem type for the RootFS."

				# User must unmount partition before format
				elif (( ${aDRIVE_ISMOUNTED[$MENU_DRIVE_INDEX]} )); then

					G_WHIP_MSG '[WARNING] Partition must be unmounted, before format and RootFS transfer can begin.\n\nPlease unmount the partition, then try again.'

				else

					if G_WHIP_YESNO 'This process will move the root filesystem data to another location. This may increase R/W performance when using a USB drive over SDcard, however, there are some limitations:
\n - The SD/eMMC card, which holds kernel and bootloader, is still required for the boot process. On RPi3, which supports full USB boot, instead it is recommended to flash the whole DietPi image to a USB drive and boot the system without SDcard.
\n - Custom software installs might use info of the old root mount/filesystem, hence we recommend to move the root file system on fresh DietPi systems only.
\n - An immediate reboot is done after the transfer has successfully finished to assure that fstab and cmdline cannot be reverted.
\nDo you wish to continue?'; then

						G_WHIP_MSG 'On the next screen, you will be asked to format the target partition.
\nPlease see the following recommendations for RootFS target filesystem type:
\n - Odroid: RootFS transfer supports ONLY EXT4 format\n\n - RPi: RootFS transfer supports EXT4, BTRFS and F2FS'

						# NB: We dont enter main loop in this func
						TARGETMENUID=2
						while (( $TARGETMENUID == 2 ))
						do

							Menu_Format

						done

						(( $FORMAT_COMPLETED )) && RootFS_Move

					fi

				fi

			elif [[ $G_WHIP_RETURNED_VALUE == 'Read Only' ]]; then

				# Disallow if userdata is located on this drive!
				if (( $partition_contains_swapfile )); then

					Notification 1

				else

					Toggle_WriteMode

				fi

			elif [[ $G_WHIP_RETURNED_VALUE == 'Benchmark' ]]; then

				(( $SERVICES_STOPPED )) || { /boot/dietpi/dietpi-services stop; SERVICES_STOPPED=1; }
				FP_BENCHFILE=${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} /boot/dietpi/func/dietpi-benchmark 1

			elif [[ $G_WHIP_RETURNED_VALUE == 'Check & Repair' ]]; then

				if [[ ${aDRIVE_MOUNT_TARGET[$MENU_DRIVE_INDEX]} == '/' ]]; then

					if G_WHIP_YESNO 'The root file system can only be checked on reboot.
\nDo you want to force a file system check of root partition on next reboot?
\nNB: Logs can be found after reboot either via "journalctl -t systemd-fsck" or "cat /run/initramfs/fsck.log"'; then

						> /forcefsck
						G_WHIP_YESNO 'Do you want to reboot now?' && reboot

					fi
					return

				fi

				if (( ${aDRIVE_ISMOUNTED[$MENU_DRIVE_INDEX]} )); then

					G_WHIP_YESNO 'For safe check and repair, the drive needs to be unmounted.
\nDo you want to continue with the drive being unmounted automatically?' || { TARGETMENUID=1; return; }

					(( $SERVICES_STOPPED )) || { /boot/dietpi/dietpi-services stop; SERVICES_STOPPED=1; }

					# Disable swap
					(( $partition_contains_swapfile )) && G_EXEC swapoff -a

					# Unmount drive
					G_EXEC umount "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}"

				fi

				if [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} =~ 'ext' ]]; then

					local fsck_dry='e2fsck -n -f'
					local fsck_fix='e2fsck -y -f'

				elif [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} == 'exfat' ]]; then

					G_AG_CHECK_INSTALL_PREREQ exfat-utils exfat-fuse
					local fsck_fix='exfatfsck'

				elif [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} =~ 'fat' ]]; then

					G_AG_CHECK_INSTALL_PREREQ dosfstools
					local fsck_dry='fsck.fat -n'
					local fsck_fix='fsck.fat -y'

				elif [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} == 'ntfs' ]]; then

					G_AG_CHECK_INSTALL_PREREQ ntfs-3g
					local fsck_dry='ntfsfix -n'
					local fsck_fix='ntfsfix'

				elif [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} =~ 'hfs' ]]; then

					G_AG_CHECK_INSTALL_PREREQ hfsplus hfsprogs hfsutils
					local fsck_dry='fsck.hfsplus -n -f'
					local fsck_fix='fsck.hfsplus -y -f'

				elif [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} == 'f2fs' ]]; then

					G_AG_CHECK_INSTALL_PREREQ f2fs-tools
					local fsck_dry='fsck.f2fs'
					local fsck_fix='fsck.f2fs -f'

				elif [[ ${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]} == 'btrfs' ]]; then

					G_AG_CHECK_INSTALL_PREREQ btrfs-progs
					local fsck_dry='btrfs check --readonly'
					local fsck_fix='btrfs check --repair'

				else

					G_WHIP_MSG "File system checks are currently not supported for '${aDRIVE_FSTYPE[$MENU_DRIVE_INDEX]}'. Aborting..."
					TARGETMENUID=1
					return

				fi

				# Do dry-run first for file systems supporting it:
				if [[ $fsck_dry ]]; then

					G_WHIP_MSG "The following drive will now be checked for errors:\n${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}\n\nNo repair will be completed during this process. An option to repair the drive, will be provided after the check."
					$fsck_dry "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}" &> .fsck_out_tmp
					log=1 G_WHIP_VIEWFILE .fsck_out_tmp
					rm .fsck_out_tmp

				fi

				if G_WHIP_YESNO "Would you like to run an automated repair on the following drive now?\n       ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}\n
NB:
- Automated repair steps potentially lead to data loss, which would have been able to recover by professional drive recovery services.
- If the data is extremely important and you don't have any backup, you might want to hand the drive to a recovery service as is.
- These services are usually very expensive, but might be able to recover more data than (after) this automated repair steps!"; then

					$fsck_fix "${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]}" &> .fsck_out_tmp
					log=1 G_WHIP_VIEWFILE .fsck_out_tmp
					rm .fsck_out_tmp

				fi

				# Remount all drives
				G_EXEC mount -a

				# Re-enable swap
				(( $partition_contains_swapfile )) && G_EXEC swapon -a

			elif [[ $G_WHIP_RETURNED_VALUE == 'I/O Scheduler' ]]; then

				local udev_rules='/etc/udev/rules.d/99-dietpi-io_schedulers.rules'
				[[ -f $udev_rules ]] && grep -q "KERNEL==\"${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}\"" $udev_rules && G_WHIP_MENU_ARRAY=('Reset' ': Reset to system defaults') || G_WHIP_MENU_ARRAY=()
				for i in "${aio_schedulers[@]}"; do G_WHIP_MENU_ARRAY+=("$i"); done
				G_WHIP_DEFAULT_ITEM=$io_scheduler_current
				if G_WHIP_MENU "Please select an I/O scheduler.\n
NB: This will apply to the whole drive: /dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}
    It always applies to the drive with this dev name, as well if naming changes due to new attached drives.\n
Read more about I/O scheduling: https://wiki.archlinux.org/index.php/Improving_performance#Input/output_schedulers"; then

					if [[ $G_WHIP_RETURNED_VALUE == 'Reset' ]]; then

						sed -i "/KERNEL==\"${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}\"/d" $udev_rules
						grep -q '^ACTION' $udev_rules || rm $udev_rules
						G_WHIP_YESNO '[ INFO ] A reboot is required to reset the I/O scheduler to default.\n\nDo you wish to reboot now?' && reboot

					else

						[[ -f $udev_rules ]] || echo '# Please run "dietpi-drive_manager" to adjust I/O schedulers' > $udev_rules
						G_CONFIG_INJECT "ACTION==\"add\|change\", KERNEL==\"${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}\"" "ACTION==\"add|change\", KERNEL==\"${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}\", ATTR{queue/scheduler}=\"$G_WHIP_RETURNED_VALUE\"" $udev_rules
						echo "$G_WHIP_RETURNED_VALUE" > "$io_schedulers"

					fi

				fi

			fi

		else

			TARGETMENUID=0 # Exit

		fi

	}

	Menu_Format(){

		FORMAT_COMPLETED=0

		local partition_table_text='GPT'
		(( $FORMAT_GPT )) || partition_table_text='MBR'

		local format_mode_text='Drive'
		(( $FORMAT_MODE )) && format_mode_text='Partition'

		local format_type_text='EXT4'
		if (( $FORMAT_FILESYSTEM_TYPE == 1 )); then

			format_type_text='NTFS'

		elif (( $FORMAT_FILESYSTEM_TYPE == 2 )); then

			format_type_text='FAT32'

		elif (( $FORMAT_FILESYSTEM_TYPE == 3 )); then

			format_type_text='HFS+'

		elif (( $FORMAT_FILESYSTEM_TYPE == 4 )); then

			format_type_text='BTRFS'

		elif (( $FORMAT_FILESYSTEM_TYPE == 5 )); then

			format_type_text='F2FS'

		elif (( $FORMAT_FILESYSTEM_TYPE == 6 )); then

			format_type_text='exFAT'

		fi

		G_WHIP_MENU_ARRAY=()

		# Has partition table, offer to format single partition or whole drive
		if (( ${aDRIVE_ISPARTITIONTABLE[$MENU_DRIVE_INDEX]} )); then

			G_WHIP_MENU_ARRAY+=('Format Mode' ": [$format_mode_text]")

		# No partition table, force drive wipe
		else

			FORMAT_MODE=0

		fi

		(( $FORMAT_MODE )) || G_WHIP_MENU_ARRAY+=('Partition Type' ": [$partition_table_text]")
		G_WHIP_MENU_ARRAY+=('Filesystem Type' ": [$format_type_text]")
		G_WHIP_MENU_ARRAY+=('Format' ': Start the format process with selected options')

		G_WHIP_DEFAULT_ITEM=$MENU_LASTITEM
		G_WHIP_BUTTON_CANCEL_TEXT='Back'
		if G_WHIP_MENU 'Please select formatting options:'; then

			MENU_LASTITEM=$G_WHIP_RETURNED_VALUE

			if [[ $G_WHIP_RETURNED_VALUE == 'Partition Type' ]]; then

				G_WHIP_BUTTON_OK_TEXT='MBR'
				G_WHIP_BUTTON_CANCEL_TEXT='GPT'
				G_WHIP_YESNO 'Would you like to use GPT or MBR parition table?\n - GPT is required for 2TB+ drives\n - MBR does NOT support 2TB+ drives\n\nIf unsure, select GPT (default)' && FORMAT_GPT=0 || FORMAT_GPT=1

			elif [[ $G_WHIP_RETURNED_VALUE == 'Format Mode' ]]; then

				G_WHIP_MENU_ARRAY=(

					'Partition' ": ${aDRIVE_MOUNT_SOURCE[$MENU_DRIVE_INDEX]} | UUID=${aDRIVE_UUID[$MENU_DRIVE_INDEX]}"
					'Drive' ": /dev/${aDRIVE_SOURCE_DEVICE[$MENU_DRIVE_INDEX]}"

				)

				if G_WHIP_MENU 'Please select a formatting mode:\n\n - Formatting the drive will DELETE all data on the DRIVE.\n - Formatting the partition, will DELETE all data on the current PARTITION only.\n
NB: If you are planning to dedicate the drive to this system, it is recommended to format the whole drive where possible, this will ensure full drive capacity is available for use.'; then

					[[ $G_WHIP_RETURNED_VALUE == 'Partition' ]] && FORMAT_MODE=1 || FORMAT_MODE=0

				fi

			elif [[ $G_WHIP_RETURNED_VALUE == 'Filesystem Type' ]]; then

				G_WHIP_MENU_ARRAY=(

					'0' ': EXT4   | Default (Recommended)'
					'1' ': NTFS   | Windows (High CPU usage)'
					'2' ': FAT32  | All OS (4GB filesize limit)'
					'3' ': HFS+   | Mac OS X (Intel Mac default file system)'
					'4' ': BTRFS  | Linux (Modern filesystem)'
					'5' ': F2FS   | Linux (Flash filesystem)'
					'6' ': exFAT  | Windows (Flash filesystem)'

				)

				G_WHIP_DEFAULT_ITEM=$FORMAT_FILESYSTEM_TYPE
				if G_WHIP_MENU 'Please select a filesystem type for this format:\n\nEXT4:\nHighly recommended if you plan to use this drive solely on this system (dedicated drive).\n
NTFS:\nRecommended if you plan to use this drive on a Windows system. High CPU usage during transfers.\n\nFull list of different filesystem types:\nhttps://dietpi.com/docs/dietpi_tools/#dietpi-drive-manager'; then

					# Install FS pre-reqs
					# - NTFS
					if (( $G_WHIP_RETURNED_VALUE == 1 )); then

						G_AG_CHECK_INSTALL_PREREQ ntfs-3g

					# - FAT32
					elif (( $G_WHIP_RETURNED_VALUE == 2 )); then

						G_AG_CHECK_INSTALL_PREREQ dosfstools

					# - HFS+
					elif (( $G_WHIP_RETURNED_VALUE == 3 )); then

						G_AG_CHECK_INSTALL_PREREQ hfsplus hfsprogs hfsutils

					# - Btrfs
					elif (( $G_WHIP_RETURNED_VALUE == 4 )); then

						G_AG_CHECK_INSTALL_PREREQ btrfs-progs

 					# - F2FS
					elif (( $G_WHIP_RETURNED_VALUE == 5 )); then

						G_AG_CHECK_INSTALL_PREREQ f2fs-tools

					# - exFAT
					elif (( $G_WHIP_RETURNED_VALUE == 6 )); then

						G_AG_CHECK_INSTALL_PREREQ exfat-utils exfat-fuse

					fi

					FORMAT_FILESYSTEM_TYPE=$G_WHIP_RETURNED_VALUE

				fi

			elif [[ $G_WHIP_RETURNED_VALUE == 'Format' ]]; then

				Run_Format
				TARGETMENUID=1

			fi

		else

			TARGETMENUID=1

		fi

	}

	Mount_Samba(){

		local fp_tmp='.samba_mount_out'
		local samba_clientname='192.168.'
		local samba_clientshare=''
		local samba_clientuser=''
		local samba_clientpassword=''
		local samba_fp_mount_target='samba'

		# Remove info file and default mount dir
		[[ -f '/mnt/samba/readme.txt' ]] && rm /mnt/samba/readme.txt
		[[ -d '/mnt/samba' ]] && rmdir --ignore-fail-on-non-empty /mnt/samba

		# User inputs
		G_WHIP_DEFAULT_ITEM=$samba_clientname
		G_WHIP_INPUTBOX 'Please enter the fileservers IP address\n - eg: 192.168.0.2' || return
		samba_clientname=$G_WHIP_RETURNED_VALUE

		G_WHIP_DEFAULT_ITEM=$samba_clientshare
		G_WHIP_INPUTBOX 'Please enter the fileservers shared folder name\n - eg: MySharedFolder' || return
		samba_clientshare=$G_WHIP_RETURNED_VALUE

		G_WHIP_DEFAULT_ITEM=$samba_clientuser
		G_WHIP_INPUTBOX 'Please enter the fileservers username\n - eg: JoeBloggs' || return
		samba_clientuser=$G_WHIP_RETURNED_VALUE

		G_WHIP_PASSWORD 'Please enter the fileservers password\n - eg: LetMeIn' || return
		samba_clientpassword=$result
		unset result

		G_WHIP_DEFAULT_ITEM=$samba_fp_mount_target
		G_WHIP_INPUTBOX 'Please enter a unique folder name for the mount location (eg: samba). This will be placed in /mnt/' || return
		# - Remove leading "/mnt/" and "/" in case entered by user
		samba_fp_mount_target=${G_WHIP_RETURNED_VALUE#/mnt/}; samba_fp_mount_target=${samba_fp_mount_target#/}
		# - Remove trailing slash and (re-)add "/mnt/" for full mount target path
		samba_fp_mount_target="/mnt/${samba_fp_mount_target%/}"
		# - Replace all spaces (+ tabs etc) with "_"
		samba_fp_mount_target=${samba_fp_mount_target//[[:space:]]/_}

		# Unmount if connected
		umount "$samba_fp_mount_target" 2> /dev/null

		G_AG_CHECK_INSTALL_PREREQ smbclient cifs-utils

		# Mount now
		G_EXEC mkdir -p "$samba_fp_mount_target"
		> $fp_tmp

		# - Mount attempts from highest CIFS version to lowest, until one succeeds
		#	3.1.1 supported on Buster only: https://manpages.debian.org/buster/cifs-utils/mount.cifs.8.en.html#OPTIONS
		local i
		for i in '3.1.1' '3.0' '2.1' '2.0' '1.0'
		do

			G_DIETPI-NOTIFY 2 "Attempting to mount with CIFS version: $i"
			if mount -t cifs -o username="$samba_clientuser",password="$samba_clientpassword",iocharset=utf8,uid=dietpi,gid=dietpi,file_mode=0770,dir_mode=0770,vers=$i "//$samba_clientname/$samba_clientshare" "$samba_fp_mount_target" &>> $fp_tmp; then

				# Apply to fstab
				sed -i "\#[[:space:]]${samba_fp_mount_target}[[:space:]]#d" /etc/fstab
				# - NB: Convert spaces to '\040': https://github.com/MichaIng/DietPi/issues/1201#issuecomment-339720271
				echo "//$samba_clientname/${samba_clientshare//[[:space:]]/\\040} $samba_fp_mount_target cifs username=$samba_clientuser,password=$samba_clientpassword,iocharset=utf8,uid=dietpi,gid=dietpi,file_mode=0770,dir_mode=0770,vers=$i,nofail,noauto,x-systemd.automount" >> /etc/fstab

				MENU_DRIVE_TARGET=$samba_fp_mount_target
				Init_Drives_and_Refresh
				TARGETMENUID=1

				G_WHIP_MSG "Mount completed. The new mount can be accessed via:\n - $samba_fp_mount_target\n - CIFS vers=$i"
				rm $fp_tmp
				return 0

			fi

		done

		# Failure
		sed -i '1i\[FAILED] Samba mount failed with the following error output:\n' $fp_tmp
		G_WHIP_VIEWFILE $fp_tmp
		rm $fp_tmp
		rmdir --ignore-fail-on-non-empty "$samba_fp_mount_target"

	}

	Mount_NFS(){

		local fp_tmp='.nfs_mount_out'
		local nfs_server_ip='192.168.'
		local nfs_fp_mount_target='nfs_client'
		# Server-side path to share, needed for NFSv3 only
		local nfs_fp_server_share='/'

		# Remove info file and default mount dir
		[[ -f '/mnt/nfs_client/readme.txt' ]] && rm /mnt/nfs_client/readme.txt
		[[ -d '/mnt/nfs_client' ]] && rmdir --ignore-fail-on-non-empty /mnt/nfs_client

		# User inputs
		G_WHIP_DEFAULT_ITEM=$nfs_server_ip
		G_WHIP_INPUTBOX 'Please enter the NFS servers IP address (eg: 192.168.0.2).' || return
		nfs_server_ip=$G_WHIP_RETURNED_VALUE

		G_WHIP_DEFAULT_ITEM=$nfs_fp_mount_target
		G_WHIP_INPUTBOX 'Please enter a unique folder name for the mount location (eg: nfs_client). This will be placed in /mnt/
\nNB: Please avoid white spaces or special characters for compatibility reasons.' || return
		# - Remove leading "/mnt/" and "/" in case entered by user
		nfs_fp_mount_target=${G_WHIP_RETURNED_VALUE#/mnt/}; nfs_fp_mount_target=${nfs_fp_mount_target#/}
		# - Remove trailing slash and (re-)add "/mnt/" for full mount target path
		nfs_fp_mount_target="/mnt/${nfs_fp_mount_target%/}"
		# - Replace all spaces (+ tabs etc) with "_"
		nfs_fp_mount_target=${nfs_fp_mount_target//[[:space:]]/_}

		# Unmount if connected
		umount "$nfs_fp_mount_target" 2> /dev/null

		G_AG_CHECK_INSTALL_PREREQ nfs-common netbase

		# Mount now
		G_EXEC mkdir -p "$nfs_fp_mount_target"
		> $fp_tmp

		if mount -vt nfs -o port=2049 "$nfs_server_ip":/ "$nfs_fp_mount_target" &>> $fp_tmp; then

			# Apply to fstab
			sed -i "\#[[:space:]]${nfs_fp_mount_target}[[:space:]]#d" /etc/fstab
			echo "$nfs_server_ip:/ $nfs_fp_mount_target nfs nofail,noauto,x-systemd.automount" >> /etc/fstab

			MENU_DRIVE_TARGET=$nfs_fp_mount_target
			Init_Drives_and_Refresh
			TARGETMENUID=1

			G_WHIP_MSG "Mount completed. The new mount can be accessed via:\n - $nfs_fp_mount_target"
			rm $fp_tmp
			return 0

		# Detect NFSv3 server, which requires server side path to share directory:
		elif grep -q '[[:blank:]]vers 3[[:blank:]]' $fp_tmp &&
			grep -q 'access denied by server while mounting' $fp_tmp; then

			G_WHIP_DEFAULT_ITEM=$nfs_fp_server_share
			G_WHIP_INPUTBOX 'Your NFS server appears to support NFSv3 only. Mounting NFSv3 shares requires the shares path server side to be entered.
\nPlease enter the absolute path to the share on the NFS server (eg: /mnt/nfs_share).' || return
			nfs_fp_server_share=$G_WHIP_RETURNED_VALUE

			if mount -vt nfs -o port=2049 "$nfs_server_ip:$nfs_fp_server_share" "$nfs_fp_mount_target" &>> $fp_tmp; then

				# Apply to fstab
				sed -i "\#[[:space:]]${nfs_fp_mount_target}[[:space:]]#d" /etc/fstab
				echo "$nfs_server_ip:$nfs_fp_server_share $nfs_fp_mount_target nfs nofail,noauto,x-systemd.automount" >> /etc/fstab

				MENU_DRIVE_TARGET=$nfs_fp_mount_target
				Init_Drives_and_Refresh
				TARGETMENUID=1

				G_WHIP_MSG "Mount completed. The new mount can be accessed via:\n - $nfs_fp_mount_target"
				rm $fp_tmp
				return 0

			fi

		fi

		# Failure
		sed -i '1i\[FAILED] NFS mount failed with the following error output:\n' $fp_tmp
		G_WHIP_VIEWFILE $fp_tmp
		rm $fp_tmp
		rmdir --ignore-fail-on-non-empty "$nfs_fp_mount_target"

	}

	Menu_Add_Network_Drive(){

		G_WHIP_MENU_ARRAY=(

			'Samba' ': Setup a connection for a Samba/SMB/CIFS/Windows compatible file share'
			'NFS' ': Setup a connection for a NFS compatible file share'

		)

		G_WHIP_MENU 'Please select the network mount protocol:' && "Mount_$G_WHIP_RETURNED_VALUE" || TARGETMENUID=0 # Main menu

	}

	Menu_Select_Mount_Location(){

		local fp_mount_selection='/tmp/dietpi-drive_manager_selmnt'

		# Remove last selection
		[[ -f $fp_mount_selection ]] && rm $fp_mount_selection

		# Generate menu
		G_WHIP_MENU_ARRAY=()

		while read -r line
		do

			G_WHIP_MENU_ARRAY+=("$(mawk '{print $6}' <<< "$line")" ": $(mawk '{print $1" | size: "$2"iB | available: "$4"iB"}' <<< "$line")")

		done < <(df -Ph | tail -n +2 | sed -E '/(^udev|tmpfs)/d')

		G_WHIP_MENU 'Please select a mount location to use:' || exit 1

		G_DIETPI-NOTIFY 0 "Drive mount selected: $G_WHIP_RETURNED_VALUE"
		echo "$G_WHIP_RETURNED_VALUE" > $fp_mount_selection
		exit 0

	}

	Menu_Exit(){

		TARGETMENUID=0 # Main menu
		G_WHIP_SIZE_X_MAX=50
		G_WHIP_YESNO "Exit $G_PROGRAM_NAME?" && TARGETMENUID=-1 # Exit

	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Main Loop
	#/////////////////////////////////////////////////////////////////////////////////////
	# Mode 1: Select an active mount and return value, exits from within function
	(( $INPUT == 1 )) && Menu_Select_Mount_Location
	#-----------------------------------------------------------------------------------
	# Init drives, generate /etc/fstab based on current drive mounts
	Init_Drives_and_Refresh
	#-----------------------------------------------------------------------------------
	# Mode 4: Force reset/clean fstab and exit (PREP)
	# Else  : Run menus
	if [[ $INPUT != [34] ]]; then

		until (( $TARGETMENUID < 0 ))
		do

			G_TERM_CLEAR

			if (( $TARGETMENUID == 1 )); then

				Menu_Drive

			elif (( $TARGETMENUID == 2 )); then

				Menu_Format

			elif (( $TARGETMENUID == 3 )); then

				Menu_Add_Network_Drive

			else

				Menu_Main

			fi

		done

	fi
	#-----------------------------------------------------------------------------------
	exit $EXIT_CODE
	#-----------------------------------------------------------------------------------
}
